﻿using System;
using UnityEngine;
using xuni;

namespace xres
{

    public class BundleAssetLoader : BaseLoader
    {
        public const string Err_Load_Bundle = "Err_Load_Bundle";
        public const string Err_Load_Asset = "Err_Load_Asset";


        /// <summary>
        /// 这个资源所属的Bundle
        /// </summary>
        readonly BundleLoader _bundleLoader;

        /// <summary>
        /// 资源在bundle中的路径
        /// </summary>
        internal readonly string _assetName = "";

        /// <summary>
        /// 资源类型
        /// </summary>
        readonly Type _assetType;

        LoadMode _loadMode = LoadMode.Init;

        AsyncOperation _op;

        /// <summary>
        /// 最终加载到的资源
        /// </summary>
        public UnityEngine.Object asset { get; internal set; }


        public BundleAssetLoader(BundleLoader loader, string assetName, Type assetType)
        {
            _bundleLoader = loader;
            _assetName = assetName;
            _assetType = assetType;
        }

        //==================== Unload

        /*
        bool CanUnload()
        {
            if (LoaderState.Finish != _state) //todo: 正在加载中允许释放吗?
            {
                return false;
            }

            //===== 自己是否可释放
            var ct = GetRefPagesCount();
            if (ct >= 0)
            {
                Debug.Log($"asset: has ref pages: {ct}, can't unload");
                return false;
            }

            return true;
        }

        public void Unload()
        {
            if (!CanUnload())
                return;

            _Unload();
        }
        */

        internal void _DoUnload()
        {
            if (null != asset)
            {
                if (asset is GameObject)
                {
                    GameObject.DestroyImmediate(asset, true);
                }
                else
                {
                    Resources.UnloadAsset(asset);
                }
                asset = null;
            }
            _state = LoaderState.Init;
        }

        //====================

        public float CheckProgress()
        {
            if (LoadMode.Sync == _loadMode)
                return CheckProgressAsyncMode();
            
            return CheckProgressAsyncMode();
        }

        float CheckProgressAsyncMode()
        {
            switch (_state)
            {
            case LoaderState.Init:
                return 0;

            case LoaderState.LoadAssetBundle:
                return _bundleLoader.CheckProgress() * 0.5f;

            case LoaderState.LoadAsset:
                return 0.5f + _op.progress;
            }

            return 1;
        }

        float CheckProgressSyncMode()
        {
            switch (_state)
            {
            case LoaderState.Init:
                return 0;

            case LoaderState.LoadAssetBundle:
                return _bundleLoader.CheckProgress();
            }

            return 1;
        }

        public void Load(bool async)
        {
            if (LoaderState.Finish == _state && string.IsNullOrEmpty(ErrType)) //成功加载后就不再加载
                return;

            switch (_state)
            {
            case LoaderState.Init:
            case LoaderState.Finish:
                var mode = async ? LoadMode.Async : LoadMode.Sync;
                _loadMode = mode;
                _Load(async);
                return;
            }

            //加载中切换加载模式无效
        }

        void _Load(bool async)
        {
            if (async)
                LoadAsync();
            else
                LoadSync();
        }

        void LoadSync()
        {
            if (LoaderState.Init == _bundleLoader._state)
                _bundleLoader.Load(false);
            else if (LoaderState.Finish != _bundleLoader._state)
                throw new Exception($"BundleAssetLoader: asset's bundle load in async mode: {_bundleLoader._bundleName}");

            ProfilerUtils.SwBegin();
            asset = _bundleLoader.bundle.LoadAsset(_assetName, _assetType);
            ProfilerUtils.SwEndAndDump(_assetName, 100);

            if (null == asset)
            {
                SetError(Err_Load_Asset, $"asset load fail: {_assetName}: {_bundleLoader._bundleName}");
                Debug.LogError(ErrMsg);
                SetFinishAndNotify(ErrMsg);
                return;
            }

            SetError("", "");
            SetFinishAndNotify("");
        }

        void LoadAsync()
        {
            if (_bundleLoader.IsLoadFinish())
            {
                OnComplete_Bundle(null, "", null);
            }
            else
            {
                Debug.Log($"BundleAssetLoader: asset's bundle not loaded, wait it: {_bundleLoader._bundleName}");
                _state = LoaderState.LoadAssetBundle;
                _bundleLoader.onComplete.AddListener(OnComplete_Bundle);
                _bundleLoader.Load(true);
            }
        }

        void OnComplete_Bundle(object obj, string type, object data)
        {
            //Debug.Log($"BundleAssetLoader: asset's bundle finish");

            if (!string.IsNullOrEmpty(_bundleLoader.ErrMsg))
            {
                SetError(Err_Load_Bundle, $"asset's bundle load fail");
                Debug.LogError(ErrMsg);
                //asset = null;
                SetFinishAndNotify(ErrMsg);
                return;
            }

            LoadAsset();
        }

        void LoadAsset()
        {
            _state = LoaderState.LoadAsset;
            var request = _bundleLoader.bundle.LoadAssetAsync(_assetName, _assetType);
            _op = request;
            request.completed += OnComplete_Asset;
        }

        void OnComplete_Asset(AsyncOperation op)
        {
            _op = null;

            var request = (AssetBundleRequest)op;
            if (null == request.asset)
            {
                SetError(Err_Load_Asset, $"asset load fail");
                Debug.LogError(ErrMsg);
                //asset = null;
                SetFinishAndNotify(ErrMsg);
                return;
            }

            //Debug.Log($"asset load ok");
            asset = request.asset;

            SetError("", "");
            SetFinishAndNotify("");
        }

    } //end of class


}

